﻿using System;
using System.Diagnostics;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace YanZhiwei.DotNet2.Utilities.Common
{
    /// <summary>
    /// ObjectId 帮助类
    /// <para>参考：http://www.cnblogs.com/gaochundong/archive/2013/04/24/csharp_generate_mongodb_objectid.html </para>
    /// </summary>
    internal static class ObjectIdHelper
    {
        private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        private static readonly object innerLock = new object();
        private static int counter;
        private static readonly byte[] machineHash = GenerateHostHash();
        private static readonly byte[] processId = BitConverter.GetBytes(GenerateProcessId());

        public static byte[] Generate()
        {
            byte[] _oid = new byte[12];
            int  _copyidx = 0;

            Array.Copy(BitConverter.GetBytes(GenerateTime()), 0, _oid, _copyidx, 4);
            _copyidx += 4;

            Array.Copy(machineHash, 0, _oid, _copyidx, 3);
            _copyidx += 3;

            Array.Copy(processId, 0, _oid, _copyidx, 2);
            _copyidx += 2;

            Array.Copy(BitConverter.GetBytes(GenerateCounter()), 0, _oid, _copyidx, 3);

            return _oid;
        }

        private static int GenerateTime()
        {
            DateTime _now = DateTime.UtcNow;
            DateTime _nowtime = new DateTime(epoch.Year, epoch.Month, epoch.Day,
              _now.Hour, _now.Minute, _now.Second, _now.Millisecond);
            TimeSpan _diff = _nowtime - epoch;
            return Convert.ToInt32(Math.Floor(_diff.TotalMilliseconds));
        }

        private static byte[] GenerateHostHash()
        {
            using (MD5 md5 = MD5.Create())
            {
                string host = Dns.GetHostName();
                return md5.ComputeHash(Encoding.Default.GetBytes(host));
            }
        }

        private static int GenerateProcessId()
        {
            var _process = Process.GetCurrentProcess();
            return _process.Id;
        }
        private static int GenerateCounter()
        {
            lock (innerLock)
            {
                return counter++;
            }
        }
    }
}
